﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Dynamic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Facebook;
using GDFlacTool.Common;

namespace GDFlacTool.Facebook
{
    public partial class FormFacebookPost : Form
    {
        private readonly TaskScheduler _ui;
        private readonly string _accessToken;
        private List<Friend> _friends;
        private double _friendCount;
        private string _lastMessageId;
        private readonly IList<Track> _tracks;
        private readonly string _totalTime;

        public FormFacebookPost(string accessToken, IList<Track> tracks, string totalTime)
        {
            _accessToken = accessToken;
            _ui = TaskScheduler.FromCurrentSynchronizationContext();
            _tracks = tracks;
            _totalTime = totalTime;

            InitializeComponent();

            if (_tracks[0].HasFileCoverArt)
            {
                picCoverArt.Image = _tracks[0].ArtImage;
            }

            txtMessage.Text = CreateMessage();
        }

        private void btnLogout_Click(object sender, EventArgs e)
        {
            var fb = new FacebookClient();

            var logoutUrl = fb.GetLogoutUrl(new
            {
                next = "https://www.facebook.com/connect/login_success.html",
                access_token = _accessToken
            });

            var webBrowser = new WebBrowser();
            webBrowser.Navigated += (o, args) =>
            {
                if (args.Url.AbsoluteUri == "https://www.facebook.com/connect/login_success.html")
                    Close();
            };

            webBrowser.Navigate(logoutUrl.AbsoluteUri);
        }

        private void FormFacebookPost_Load(object sender, EventArgs e)
        {
            GetFriends();
            GetUserProfilePicture();
            GetUserProfileInfo();
        }

        private void GetUserProfilePicture()
        {
            try
            {
                var fb = new FacebookClient(_accessToken);

                // Note: the result can either me IDictionary<string,object> or IList<object>
                // json objects with properties can be casted to IDictionary<string,object> or IDictionary<string,dynamic>
                // json arrays can be casted to IList<object> or IList<dynamic>

                // for this particular request we can guarantee that the result is
                // always IDictionary<string,object>.
                var result = (IDictionary<string, object>)fb.Get("me");

                // make sure to cast the object to appropriate type
                var id = (string)result["id"];

                // FacebookClient's Get/Post/Delete methods only supports JSON response results.
                // For non json results, you will need to use different mechanism,

                // here is an example for pictures.
                // available picture types: square (50x50), small (50xvariable height), large (about 200x variable height) (all size in pixels)
                // for more info visit http://developers.facebook.com/docs/reference/api
                string profilePictureUrl = string.Format("https://graph.facebook.com/{0}/picture?type={1}", id, "large");

                picProfile.LoadAsync(profilePictureUrl);
            }
            catch (FacebookApiException ex)
            {
                string msg = string.Format("There was an error getting user image from Facebook\n\n{0}", ex.Message);

                MessageBox.Show(this, msg, @"Facebook Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void GetUserProfileInfo()
        {
            try
            {
                var fb = new FacebookClient(_accessToken);

                dynamic result = fb.Get("me");

                dynamic id = result.id;
                dynamic name = result.name;
                dynamic firstName = result.first_name;
                dynamic lastName = result.last_name;
                dynamic gender = result.gender;
                dynamic location = result.location.name;
                
                lnkLinkInfo.Text = string.Format("{0}", name);
                lnkLinkInfo.LinkClicked += (o, e) => System.Diagnostics.Process.Start(result.link);
                
                lblUserIdInfo.Text = string.Format("{0}", id);
                lblFirstNameInfo.Text = string.Format("{0}", firstName);
                lblLastNameInfo.Text = string.Format("{0}", lastName);

                TextInfo ti = new CultureInfo("en-US", false).TextInfo;
                lblGenderInfo.Text = string.Format("{0}", ti.ToTitleCase(gender));

                lblLocationInfo.Text = location;
                lblFriendCountInfo.Text = _friendCount.ToString();

                
                // or using the indexer
                //dynamic firstName = result["first_name"];
                //dynamic lastName = result["last_name"];

                // checking if property exist
                var localeExists = result.ContainsKey("locale");

                // you can also cast it to IDictionary<string,object> and then check
                //var dictionary = (IDictionary<string, object>)result;
                //localeExists = dictionary.ContainsKey("locale");
            }
            catch (FacebookApiException ex)
            {
                string msg = string.Format("There was an error getting user info from Facebook\n\n{0}", ex.Message);

                MessageBox.Show(this, msg, @"Facebook Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void GetFriends()
        {
            try
            {
                var fb = new FacebookClient(_accessToken);

                fb.GetCompleted += (o, e) =>
                {
                    if (e.Error != null)
                    {

                        this.BeginInvoke(new MethodInvoker(() => MessageBox.Show(e.Error.Message)));
                                                 
                        return;
                    }

                    var result = (IDictionary<string, object>)e.GetResultData();
                    var data = (IList<object>)result["data"];

                    var result0 = ((IList<object>)((IDictionary<string, object>)data[0])["fql_result_set"]);
                    var result1 = ((IList<object>)((IDictionary<string, object>)data[1])["fql_result_set"]);

                    _friends = (from IDictionary<string, object> info in result0 select new Friend
                        {
                            Id = double.Parse(info["uid"].ToString()),
                            Name = info["name"].ToString(),
                            ImageLink = info["pic_square"].ToString(),
                            Status = info["online_presence"] != null ? info["online_presence"].ToString() : string.Empty
                        }).ToList();


                    _friendCount = (from IDictionary<string, object> info in result1 select double.Parse(info["friend_count"].ToString())).SingleOrDefault();
                };

                var query = string.Format("SELECT uid,name,pic_square,online_presence FROM user WHERE uid IN (SELECT uid2 FROM friend WHERE uid1={0}) order by name", "me()");
                var query2 = string.Format("SELECT friend_count FROM user WHERE uid={0}", "me()");
                fb.GetTaskAsync("fql", new
                {
                    q = new
                    {
                        query,
                        query2
                    }
                });
            }
            catch (FacebookApiException ex)
            {
                string msg = string.Format("There was an error getting user friends list from Facebook\n\n{0}", ex.Message);

                MessageBox.Show(this, msg, @"Facebook Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void btnPost_Click(object sender, EventArgs e)
        {
            var fb = new FacebookClient(_accessToken);

            // make sure to add event handler for PostCompleted.
            fb.PostCompleted += (o, ev) =>
            {
                // incase you support cancellation, make sure to check
                // e.Cancelled property first even before checking (e.Error!=null).
                if (ev.Cancelled)
                {
                    // for this example, we can ignore as we don't allow this
                    // example to be cancelled.

                    // you can check e.Error for reasons behind the cancellation.
                    var cancellationError = ev.Error;
                }
                else if (ev.Error != null)
                {
                    // error occurred
                    this.BeginInvoke(new MethodInvoker(() => {MessageBox.Show(ev.Error.Message);}));
                }
                else
                {
                    // the request was completed successfully
                    // now we can either cast it to IDictionary<string, object> or IList<object>
                    // depending on the type. or we could use dynamic.
                    dynamic result = ev.GetResultData();
                    _lastMessageId = result.id;

                    // make sure to be on the right thread when working with ui.
                    this.BeginInvoke(new MethodInvoker(() =>
                        {
                            string msg = string.Format("Album information was posted successfully to Facebook");

                            MessageBox.Show(this, msg, @"Facebook Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
                            txtMessage.Text = string.Empty;
                            btnDeleteLastMessage.Enabled = true;
                            btnPost.Enabled = false;
                        }));
                }
            };

            dynamic parameters = new ExpandoObject();

            if (picCoverArt.Image != null)
            {
                byte[] img = CreateThumbnailImage(picCoverArt.Image);

                var media = new FacebookMediaObject {FileName = "Album Cover", ContentType = "image/jpg"};

                media.SetValue(img);
                parameters.source = media;
            }

            parameters.message = txtMessage.Text;

            fb.PostTaskAsync("/me/photos", parameters);
        }

        private byte[] CreateThumbnailImage(Image image)
        {
            Image img = Functions.ResizeImageFromFile(image, 600, 600, true);

            return Functions.ImageToByte(img);
        }

        private string CreateMessage()
        {
            var sb = new StringBuilder();
            string trackCount = _tracks.Count.ToString(CultureInfo.InvariantCulture);
            string discCount = "n/a";
            
            if (!string.IsNullOrEmpty(_tracks[0].DiscNumber))
            {
                string[] tmp = _tracks[0].DiscNumber.Split('/');

                if (tmp.Length == 2)
                {
                    discCount = tmp[1];
                }
            }

            sb.AppendLine("Another good release tagged with cover art by GD Flac Tool");
            sb.AppendLine("-------------------------------------------------------------------------");
            sb.AppendFormat("Artist..........: {0}", _tracks[0].Artist);
            sb.AppendLine();
            sb.AppendFormat("Album.........: {0}", _tracks[0].Album);
            sb.AppendLine();
            sb.AppendFormat("Release Type: {0}", Functions.ConvertNumberToReleaseType(_tracks[0].ReleaseType));
            sb.AppendLine();
            sb.AppendFormat("Genre.........: {0}", _tracks[0].Genre);
            sb.AppendLine();
            sb.AppendFormat("Release Year: {0}", _tracks[0].Year);
            sb.AppendLine();
            sb.AppendFormat("Tracks........: {0}", trackCount);
            sb.AppendLine();
            sb.AppendFormat("Discs..........: {0}", discCount);
            sb.AppendLine();
            sb.AppendFormat("Total Time: {0}", _totalTime);
            sb.AppendLine();
            sb.AppendFormat("Comments.....: {0}", _tracks[0].Comment);
            sb.AppendLine();
            sb.AppendLine("-------------------------------------------------------------------------");
            sb.AppendLine("http://gdflactool.developtools.se/gdflactool/index.htm");

            return sb.ToString();
        }

        private void btnDeleteLastMessage_Click(object sender, EventArgs e)
        {
            btnDeleteLastMessage.Enabled = false;

            var fb = new FacebookClient(_accessToken);

            // make sure to add event handler for DeleteCompleted.
            fb.DeleteCompleted += (o, ev) =>
            {
                // incase you support cancellation, make sure to check
                // e.Cancelled property first even before checking (e.Error!=null).
                if (ev.Cancelled)
                {
                    // for this example, we can ignore as we don't allow this
                    // example to be cancelled.

                    // you can check e.Error for reasons behind the cancellation.
                    var cancellationError = ev.Error;
                }
                else if (ev.Error != null)
                {
                    // error occurred
                    this.BeginInvoke(new MethodInvoker(() =>{MessageBox.Show(ev.Error.Message);}));
                }
                else
                {
                    // the request was completed successfully

                    // make sure to be on the right thread when working with ui.
                    this.BeginInvoke(new MethodInvoker(() =>
                        {
                            string msg = string.Format("Last message was deleted successfully");

                        MessageBox.Show(this, msg, @"Facebook Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
                        btnDeleteLastMessage.Enabled = false;
                    }));
                }
            };

            fb.DeleteTaskAsync(_lastMessageId);
        }
    }

    internal class Friend
    {
        private double _id;
        private string _name = string.Empty;
        private string _imageLink = string.Empty;
        private string _status = string.Empty;

        public double Id
        {
            get { return _id; }
            set { _id = value; }
        }

        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        public string ImageLink
        {
            get { return _imageLink; }
            set { _imageLink = value; }
        }

        public string Status
        {
            get { return _status; }
            set { _status = value; }
        }
    }
}
